package com.ruoyi.generator.util;

import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.constant.GenConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.generator.config.GenConfig;
import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.domain.GenTableColumn;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.*;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.util.*;
import java.util.stream.Collectors;

/**
 * power designer - 解析pdm
 *
 * @author fansd
 */
@Slf4j
public class PdmUtils {

    private Document document;
    private Namespace oNamespace;
    private Namespace cNamespace;
    private Namespace aNamespace;
    private Element model;

    private List<String> exclusionTableList;

    public PdmUtils() {
    }

    public PdmUtils(String pdmPath) {

    }

    public PdmUtils(String pdmPath, String exclusionTable) {
        // 格式化 排除表 为list
        exclusionTableList = Arrays.asList(Convert.toStrArray(exclusionTable));

        // 通过xml解析pdm
        SAXReader saxReader = new SAXReader();
        try {
            document = saxReader.read(new File(pdmPath));
        } catch (DocumentException e) {
            e.printStackTrace();
            log.error("pdm文件不存在");
        }

        Element rootElement = document.getRootElement();
        oNamespace = new Namespace("o", "object");
        cNamespace = new Namespace("c", "collection");
        aNamespace = new Namespace("a", "attribute");

        Element rootObject = rootElement.element(new QName("RootObject", oNamespace));
        Element children = rootObject.element(new QName("Children", cNamespace));
        model = children.element(new QName("Model", oNamespace));
    }

    /**
     * 解析包中文名和包英文名称
     *
     * @return 结果
     */
    public List<GenTable> getTables() {
        //直接解析table
        List<Element> tableEles = new ArrayList<Element>();
        List<GenTable> result = new ArrayList<GenTable>(10);

        Map<String, JSONObject> packageInfo = parsePackage();

        Element tablesEle = model.element(new QName("Tables", cNamespace));

        if (tablesEle != null) {
            tableEles.addAll(tablesEle.elements(new QName("Table", oNamespace)));
        }

        for (Element tableElement : tableEles) {
            // table code属性
            String code = getTextFromEle(tableElement.element(new QName("Code", aNamespace))).toUpperCase();

            // 排除掉正则中的数据表
            List<String> exclusionTableResult = exclusionTableList.stream().filter(item -> code.startsWith(item)).collect(Collectors.toList());
            if (exclusionTableResult.size() > 0) {
                continue;
            }

            // table comment属性
            String comment = getTextFromEle(tableElement.element(new QName("Comment", aNamespace)));
            // table在pdm中的唯一标识
            String tableId = tableElement.attributeValue("Id");
            // table在pdm中的唯一标识
            String packageCode = packageInfo.get(tableId).getString("packageCode");
            packageCode = StringUtils.isNotEmpty(packageCode) ? packageCode : GenConfig.getPackageName();

            GenTable table = new GenTable();
            table.setTableName(code);
            table.setTableComment(comment);
            table.setPackageName(packageCode);
            table.setClassName(GenUtils.convertClassName(code));
            table.setModuleName(GenUtils.getModuleName(packageCode));
            table.setBusinessName(code);
            table.setFunctionName(comment);
            table.setFunctionAuthor(GenConfig.getAuthor());
            table.setCreateBy(SecurityUtils.getUsername());

            List<GenTableColumn> columns = parsetColumn(tableElement);

            table.setColumns(columns);

            SpringUtils.getBean(RedisCache.class).setCacheObject(GenConstants.GEN_KEY + code, table);

            result.add(table);
        }
        return result;
    }

    public List<GenTable> parseTable1() {
        //直接解析table
        List<Element> tableEles = new ArrayList<Element>();
//        JSONArray result = new JSONArray();

        Map<String, JSONObject> packageInfo = parsePackage();

        Element tablesEle = model.element(new QName("Tables", cNamespace));
        if (tablesEle != null) {
            tableEles.addAll(tablesEle.elements(new QName("Table", oNamespace)));
        }

        int i = 0;
        for (Element tableElement : tableEles) {
            i++;
            String tableId = tableElement.attributeValue("Id");
            Element name = tableElement.element(new QName("Name", aNamespace));
            Element code = tableElement.element(new QName("Code", aNamespace));
            Element comment = tableElement.element(new QName("Comment", aNamespace));

            GenTable table = new GenTable();
            JSONObject currentPackage = packageInfo.get(tableId);

            table.setTableName(getTextFromEle(code));
            table.setTableComment(getTextFromEle(comment));
            table.setPackageName(currentPackage.getString("packageCode"));
            table.setTableComment(currentPackage.getString("packageCode"));
        }
        return null;
    }

    private List<GenTableColumn> parsetColumn(Element tableElement) {

        List<GenTableColumn> result = new ArrayList<GenTableColumn>();

        //解析主键
        Element primaryKeyEle = tableElement.element(new QName("PrimaryKey", cNamespace));
        List<String> pkIds = new ArrayList<>();
        if (primaryKeyEle != null) {
            List<Element> pks = primaryKeyEle.elements(new QName("Key", oNamespace));
            for (Element pk1 : pks) {
                pkIds.add(pk1.attribute("Ref").getValue());
            }
        }

        Element keysEle = tableElement.element(new QName("Keys", cNamespace));
        List<String> pkColumnIds = new ArrayList<>();
        if (keysEle != null) {
            List<Element> keyEleList = keysEle.elements(new QName("Key", oNamespace));
            for (Element keyEle : keyEleList) {
                Attribute id = keyEle.attribute("Id");
                if (pkIds.contains(id.getValue())) {
                    List<Element> list = keyEle.element(new QName("Key.Columns", cNamespace)).elements(new QName("Column", oNamespace));
                    for (Element element : list) {
                        pkColumnIds.add(element.attribute("Ref").getValue());
                    }
                }
            }
        }

        //解析column
        List<Element> columns = tableElement.element(new QName("Columns", cNamespace)).elements(new QName("Column", oNamespace));
        int sort = 1;
        for (Element columnEle : columns) {

            GenTableColumn columnInfo = new GenTableColumn();

            String columnId = columnEle.attribute("Id").getValue();
            String cName = getTextFromEle(columnEle.element(new QName("Name", aNamespace)));
            String cCode = getTextFromEle(columnEle.element(new QName("Code", aNamespace)));
            String cDataType = getTextFromEle(columnEle.element(new QName("DataType", aNamespace)));
            String cLength = getTextFromEle(columnEle.element(new QName("Length", aNamespace)));
            String cComment = getTextFromEle(columnEle.element(new QName("Comment", aNamespace)));
            String nullable = getTextFromEle(columnEle.element(new QName("Column.Mandatory", aNamespace)));

            columnInfo.setColumnName(cCode.toUpperCase());
            columnInfo.setColumnComment(cComment);
            columnInfo.setColumnType(cDataType);
            columnInfo.setIsIncrement("0");
            columnInfo.setSort(sort);
            sort += 1;


            if (pkColumnIds.contains(columnId)) {
                columnInfo.setIsPk("1");
            } else {
                columnInfo.setIsPk("0");
            }

            if (StringUtils.isNotEmpty(nullable)) {
                columnInfo.setIsRequired("1");
            } else {
                columnInfo.setIsRequired("0");
            }
            if (cComment != null) {
                columnInfo.setColumnComment(cComment.replace("\n", "  "));
            }
            result.add(columnInfo);
        }
        return result;
    }

    /**
     * 解析包中文名和包英文名称
     *
     * @return 结果
     */
    private Map<String, JSONObject> parsePackage() {
        Map<String, JSONObject> result = new HashMap<String, JSONObject>(10);

        Element physicalDiagramsEle = model.element(new QName("PhysicalDiagrams", cNamespace));
        if (physicalDiagramsEle != null) {
            // 取得所有包
            List<Element> physicalDiagrams = physicalDiagramsEle.elements(new QName("PhysicalDiagram", oNamespace));

            if (physicalDiagrams != null) {
                for (Element physicalDiagram : physicalDiagrams) {
                    JSONObject packageInfo = new JSONObject();

                    // package id
                    String id = physicalDiagram.attributeValue("Id");
                    // package name
                    Element name = physicalDiagram.element(new QName("Name", aNamespace));
                    // package code
                    Element code = physicalDiagram.element(new QName("Code", aNamespace));
                    // package comment
                    Element comment = physicalDiagram.element(new QName("Comment", aNamespace));

                    packageInfo.put("packageId", id);
                    packageInfo.put("packageName", StringUtils.isNotNull(name) ? name.getText().trim() : "");
                    packageInfo.put("packageCode", StringUtils.isNotNull(code) ? code.getText().trim() : "");
                    packageInfo.put("packageComment", StringUtils.isNotNull(comment) ? comment.getText().trim() : "");

                    // 该包名下所有表格集合
                    List<Element> tables = physicalDiagram.element(new QName("Symbols", cNamespace)).elements(new QName("TableSymbol", oNamespace));

                    for (Element table : tables) {
                        // 所属tableId
                        String tableId = table.element(new QName("Object", cNamespace)).element(new QName("Table", oNamespace)).attributeValue("Ref");
                        result.put(tableId, packageInfo);
                    }
                }
            }
        }
        return result;
    }

    private String getTextFromEle(Element element) {
        if (element == null) {
            return "";
        }
        return element.getText();
    }
}
